/* eslint-disable global-require */
import fs from 'fs';
import path from 'path';
import chalk from 'chalk';
import type {
  OutputOptions,
  RollupBuild,
  RollupError,
  RollupOptions,
  RollupOutput,
  RollupWatcher,
  WarningHandlerWithDefault,
} from 'rollup';
import type { RollupReplaceOptions } from '@rollup/plugin-replace';
import type { Alias } from '@rollup/plugin-alias';
import { emptyDir, onRollupWarning } from './utils';
import { createLogger } from './logger';
import { resolveConfig } from './config';
import { resolveViteConfig } from './viteConfigAdapter';

export interface BuildOptions {
  /**
   * 项目根目录。可以是绝对路径，也可以是相对路径
   * 配置文件本身的位置
   * @default process.cwd()
   */
  root: string;
  /**
   * ant-design 样式前缀
   */
  antPrefix?: string;
  /**
   * 在开发或生产中使用时的基本公共路径
   * @default '/'
   */
  base?: string;
  /**
   * 显式设置运行模式。这将覆盖默认模式，并且可以被命令行 --mode 选项覆盖
   */
  mode?: string;
  /**
   * 目标项目运行环境（域名），必填
   */
  deepfosEnv: string;
  /**
   * 打包目标：系统/组件
   */
  target?: 'element';
  /**
   * 是否生成 sourcemap
   * @default false
   */
  sourcemap?: boolean | 'inline';
  /**
   * 是否生成可视化分析
   * @default false
   */
  analyze?: boolean;
  /**
   * 主题配置，postcss 中 modifyVars 参数
   */
  theme?: Record<string, string>;
  /**
   * 插件 @rollup/plugin-replace 参数
   */
  replace?: RollupReplaceOptions;
  /**
   * 插件 @rollup/plugin-alias 的 entries 参数
   */
  alias?: Alias;
  /**
   * 将与 rollup 汇总选项合并
   * https://rollupjs.org/guide/en/#big-list-of-options
   */
  rollupOptions?: RollupOptions;
}

/**
 * 跟踪并行构建调用，并仅在所有构建完成后停止 esbuild 服务
 */
let parallelCallCounts = 0;
// 我们使用单独的计数器进行跟踪，因为调用可能会在调用之前出错
const parallelBuilds: RollupBuild[] = [];

/**
 * 构建生产环境应用
 */
export async function build(
  inlineConfig: BuildOptions,
): Promise<RollupOutput | RollupOutput[] | RollupWatcher> {
  parallelCallCounts += 1;
  try {
    return await doBuild(inlineConfig);
  } finally {
    parallelCallCounts -= 1;
    if (parallelCallCounts <= 0) {
      await Promise.all(parallelBuilds.map((bundle) => bundle.close()));
      parallelBuilds.length = 0;
    }
  }
}

async function doBuild(
  inlineConfig: BuildOptions,
): Promise<RollupOutput | RollupOutput[] | RollupWatcher> {
  const logger = createLogger();
  // 适配 vite 配置
  const config = await resolveViteConfig(inlineConfig);
  // 生成 rollup 配置
  const options = await resolveConfig(config, logger);

  const { version } = require('../package.json');
  const mode = config.mode || 'production';
  logger.info(chalk.green(`seepfly@${version} [${mode}] 构建中...`));

  const outputBuildError = (e: RollupError) => {
    logger.error(chalk.red(`${e.plugin ? `[${e.plugin}] ` : ''}${e.message}`));
    if (e.id) {
      const loc = e.loc ? `:${e.loc.line}:${e.loc.column}` : '';
      logger.error(`file: ${chalk.cyan(`${e.id}${loc}`)}`);
    }
    if (e.frame) {
      logger.error(chalk.yellow(e.frame));
    }
  };

  try {
    const outputs = options?.output;

    const outDir = path.resolve(config.root, (options.output as OutputOptions)?.dir as string);

    if (fs.existsSync(outDir)) {
      emptyDir(outDir);
    }

    // 使用 rollup 生成文件
    const bundle = await require('rollup').rollup({
      ...options,
      onwarn(...args: Parameters<WarningHandlerWithDefault>) {
        onRollupWarning(logger, ...args);
      },
    });
    parallelBuilds.push(bundle);

    const generate = (output: OutputOptions = {}) => {
      return bundle.write({
        ...options.output,
        ...output,
      });
    };

    if (Array.isArray(outputs)) {
      const res = [];
      // eslint-disable-next-line no-restricted-syntax
      for (const output of outputs) {
        // eslint-disable-next-line no-await-in-loop
        res.push(await generate(output));
      }
      return res;
    }
    return await generate(outputs);
  } catch (e) {
    outputBuildError(e as RollupError);
    throw e;
  }
}
